💡 Paper Reference & Attribution: This article is derived from an in-depth study, engineering analysis, and architectural interpretation of the Microsoft research paper From Local to Global: A Graph RAG Approach to Query-Focused Summarization (arXiv:2404.16130).
Objective: Translate the core workflow of Microsoft GraphRAG into a practical PostgreSQL + TypeScript technology stack, unifying "Community Summaries / Community Answers / Global Answers" and "Edge Weight Calculation" into an actionable, production-ready design specification.
The end-to-end GraphRAG execution pipeline can be summarized as follows:
MERMAID
From an engineering perspective, the two most critical implementation milestones are:
In the original GraphRAG paper, a "Community" is essentially:
In software architecture, this translates to:
textGraph Community = A group of Entity Nodes + Relation Edges + Associated Claims = A semantic topic module = An independently summarizable knowledge unit
Represented in TypeScript:
tstype Community = { id: string; level: number; nodeIds: string[]; edgeIds: string[]; claimIds: string[]; summary?: string; parentCommunityId?: string; createdAt: Date; };
The GraphRAG paper explicitly highlights:
In the final stage of knowledge graph extraction, entity/relation/claim instances are merged. Repeated instances of a relationship become the weight of graph edges, and claims are aggregated similarly.
This implies that GraphRAG edge weights represent evidence frequency / connection strength, rather than an arbitrary score predicted by a standalone ranking model.
Given two entities and , with denoting the set of extracted relation instances between them, the edge weight is defined as:
Where:
If the extraction pipeline repeatedly discovers relationships such as:
A collaborates_with BA and B signed partnershipA and B jointly launched projectA works with BThese semantically equivalent instances are normalized into a unified relation type (collaborates_with) and their occurrences are accumulated.
Thus, edge weight reflects:
tstype Entity = { id: string; name: string; description?: string; }; type RelationInstance = { sourceEntityId: string; targetEntityId: string; relationType: string; // Normalized relation type, e.g., "collaborates_with" rawText: string; sourceDocId?: string; chunkId?: string; }; type GraphEdge = { sourceEntityId: string; targetEntityId: string; relationType: string; weight: number; evidenceCount: number; }; function normalizeRelationType(raw: string): string { // Normalize relation tokens: trim whitespace, lowercase, snake_case // E.g.: "works with" -> "works_with", "collaborates with" -> "collaborates_with" return raw .trim() .toLowerCase() .replace(/\s+/g, "_"); } function buildEdgeWeights( relationInstances: RelationInstance[] ): GraphEdge[] { const map = new Map<string, GraphEdge>(); for (const item of relationInstances) { const relationType = normalizeRelationType(item.relationType); const key = [item.sourceEntityId, item.targetEntityId, relationType].join("::"); const existing = map.get(key); if (existing) { existing.weight += 1; existing.evidenceCount += 1; } else { map.set(key, { sourceEntityId: item.sourceEntityId, targetEntityId: item.targetEntityId, relationType, weight: 1, evidenceCount: 1, }); } } return Array.from(map.values()); }
weight represents the cumulative corroboration count.normalizeRelationType can incorporate synonym dictionaries or embedding-based semantic canonicalization.tstype GraphNode = { id: string; label: string; description?: string; }; type Graph = { nodes: GraphNode[]; edges: GraphEdge[]; }; async function buildGraph( entityRecords: Entity[], relationInstances: RelationInstance[] ): Promise<Graph> { const nodes = entityRecords.map(e => ({ id: e.id, label: e.name, description: e.description, })); const edges = buildEdgeWeights(relationInstances); return { nodes, edges }; } function detectCommunities(graph: Graph): Community[] { // Execute hierarchical graph clustering (e.g., Leiden / Louvain algorithm) // Core concept: Cluster nodes driven by edge weights const communities: Community[] = []; // 1) Identify densely connected subgraphs according to connection weights // 2) Form modular community partitions // 3) Recursively subdivide into multi-level hierarchies return communities; }
tstype CommunitySummaryInput = { communityId: string; nodeIds: string[]; edgeIds: string[]; claimIds: string[]; }; async function summarizeCommunity( input: CommunitySummaryInput, context: string ): Promise<string> { const prompt = ` You are a specialized knowledge graph community summarizer. Generate a concise yet comprehensive structured summary based on the entities, relations, and claims within this community. Community ID: ${input.communityId} Entities: ${input.nodeIds.join(", ")} Relations: ${input.edgeIds.join(", ")} Claims: ${input.claimIds.join(", ")} Background Context: ${context} `; return await llmCall(prompt); }
Hierarchical alignment:
tstype CommunityAnswer = { communityId: string; answer: string; usefulnessScore: number; // 0 to 100 }; async function generateCommunityAnswer( query: string, communitySummary: string ): Promise<CommunityAnswer> { const prompt = ` User Query: ${query} Community Summary: ${communitySummary} Answer the query based on the summary and provide a usefulness score (0-100) indicating how relevant this community summary is to answering the query. Output Format (JSON): { "answer": "...", "usefulnessScore": 88 } `; const raw = await llmCall(prompt); const result = JSON.parse(raw); return { communityId: "", answer: result.answer, usefulnessScore: Number(result.usefulnessScore ?? 0), }; } async function generateGlobalAnswer( query: string, communityAnswers: CommunityAnswer[] ): Promise<string> { const ranked = communityAnswers .filter(x => x.usefulnessScore > 0) .sort((a, b) => b.usefulnessScore - a.usefulnessScore); const context = ranked .map(x => x.answer) .join("\n\n"); const prompt = ` User Query: ${query} Reference Candidate Answers: ${context} Synthesize the candidate answers above into a comprehensive, coherent final global response. `; return await llmCall(prompt); }
sqlCREATE TABLE entities ( id UUID PRIMARY KEY, name TEXT NOT NULL, description TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE relation_instances ( id UUID PRIMARY KEY, source_entity_id UUID NOT NULL REFERENCES entities(id), target_entity_id UUID NOT NULL REFERENCES entities(id), relation_type TEXT NOT NULL, raw_text TEXT NOT NULL, source_doc_id TEXT, chunk_id TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE graph_edges ( id UUID PRIMARY KEY, source_entity_id UUID NOT NULL REFERENCES entities(id), target_entity_id UUID NOT NULL REFERENCES entities(id), relation_type TEXT NOT NULL, weight INTEGER NOT NULL DEFAULT 1, evidence_count INTEGER NOT NULL DEFAULT 1, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE communities ( id UUID PRIMARY KEY, parent_community_id UUID REFERENCES communities(id), level INTEGER NOT NULL, summary TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE community_members ( id UUID PRIMARY KEY, community_id UUID NOT NULL REFERENCES communities(id), entity_id UUID NOT NULL REFERENCES entities(id), created_at TIMESTAMPTZ DEFAULT NOW() );
sqlWITH normalized AS ( SELECT source_entity_id, target_entity_id, LOWER(REGEXP_REPLACE(relation_type, '\\s+', '_', 'g')) AS relation_type FROM relation_instances ) SELECT source_entity_id, target_entity_id, relation_type, COUNT(*) AS weight, COUNT(*) AS evidence_count FROM normalized GROUP BY source_entity_id, target_entity_id, relation_type;
This SQL query delivers the exact database implementation of "corroborated relation frequency = graph edge weight".
GraphRAG relies on empirical occurrence counts across extracted chunks rather than complex machine-learned regression scores. Thus, leveraging relational database COUNT(*) aggregations provides high performance, complete auditability, and direct compatibility with graph partitioning algorithms.
Community detection algorithms require weighted topology to discern:
High-weight connections guide algorithms to assemble precise, information-dense topic partitions.
The end-to-end GraphRAG pipeline is organized across these core layers:
MERMAID
Key principles: